package org.tulkas.http;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.HttpHost;
import org.apache.http.impl.client.cache.CacheConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tulkas.config.BasicConfig;
import org.tulkas.config.GlobalConfig;
import org.tulkas.http.headers.RequestHeaders;
import org.tulkas.util.HttpUtil;

public class ClientOptions {
	public static final String DEFAULT_CONFIG_FILE = "internetOptions.properties";

	private static Logger logger = LoggerFactory.getLogger(ClientOptions.class);

	private static final Map<String, Class<?>> HTTP_PARAMS_MAP = new HashMap<String, Class<?>>();
	static {
		//Core protocal parameters
		HTTP_PARAMS_MAP.put("http.protocol.element-charset", String.class);
		HTTP_PARAMS_MAP.put("http.protocol.content-charset", String.class);
		HTTP_PARAMS_MAP.put("http.protocol.strict-transfer-encoding",
				Boolean.class);
		HTTP_PARAMS_MAP.put("http.protocol.expect-continue", Boolean.class);
		HTTP_PARAMS_MAP.put("http.protocol.wait-for-continue", Integer.class);

		//connection management parameters
		HTTP_PARAMS_MAP.put("http.socket.timeout", Integer.class);
		HTTP_PARAMS_MAP.put("http.tcp.nodelay", Boolean.class);
		HTTP_PARAMS_MAP.put("http.socket.buffer-size", Integer.class);
		HTTP_PARAMS_MAP.put("http.socket.linger", Integer.class);
		HTTP_PARAMS_MAP.put("http.connection.timeout", Integer.class);
		HTTP_PARAMS_MAP.put("http.connection.stalecheck", Boolean.class);
		HTTP_PARAMS_MAP.put("http.connection.max-line-length", Integer.class);
		HTTP_PARAMS_MAP.put("http.connection.max-status-linegarbage",
				Integer.class);

		//Cookie parameters
		HTTP_PARAMS_MAP
				.put("http.protocol.single-cookie-header", Boolean.class);

		//Authentication parameters
		HTTP_PARAMS_MAP.put("http.protocol.handle-authentication",
				Boolean.class);
		HTTP_PARAMS_MAP.put("http.auth.credential-charset", String.class);
		HTTP_PARAMS_MAP.put("http.auth.target-scheme-pref", String.class);
		HTTP_PARAMS_MAP.put("http.auth.proxy-scheme-pref", String.class);

		//Client parameters
		HTTP_PARAMS_MAP.put("http.protocol.max-redirects", Integer.class);
		HTTP_PARAMS_MAP.put("http.protocol.allow-circular-redirects",
				Boolean.class);
		HTTP_PARAMS_MAP.put("http.virtual-host", HttpHost.class);
	}

	private String clientName;

	private final BasicConfig config = new BasicConfig(GlobalConfig
			.getInstance());

	ClientOptions(String clientName) {
		this.clientName = clientName;
		try {
			load();
		} catch (IOException e) {
			logger.error("Error loading InternetOptions for client: "
					+ clientName, e);
		}
	}

	private HttpHost makeHttpHost(String hostString) {
		if (hostString == null)
			return null;
		try {
			String fullUrl = HttpUtil.checkAndFillHttpUrl(hostString);
			URL url = new URL(fullUrl);
			return new HttpHost(url.getHost(), url.getPort(), url.getProtocol());
		} catch (MalformedURLException e) {
			return null;
		}
	}

	public Object getHttpParam(String key) {
		Class<?> type = HTTP_PARAMS_MAP.get(key);
		String key2 = "client.options." + key;
		if (type == null) {
			return null;
		} else if (Integer.class.equals(type)) {
			return config.getInt(key2, 0);
		} else if (Boolean.class.equals(type)) {
			return config.getBoolean(key2, false);
		} else if (HttpHost.class.equals(type)) {
			return makeHttpHost(config.getString(key2, ""));
		} else {
			return config.getString(key2);
		}
	}

	public void setHttpParam(String key, Object value) {
		if (HTTP_PARAMS_MAP.containsKey(key) && value != null) {
			config.setValue("client.options." + key, value.toString());
		}
	}

	public void removeHttpParam(String key) {
		if (HTTP_PARAMS_MAP.containsKey(key))
			config.remove("client.options." + key);
	}

	public String[] getHttpParamKeys() {
		String[] keys = config.getAllKeys("client.options.http");
		ArrayList<String> list = new ArrayList<String>();
		for (String key : keys) {
			String key2 = key.substring("client.options.".length());
			if (HTTP_PARAMS_MAP.containsKey(key2))
				list.add(key2);
		}
		String[] ret = new String[list.size()];
		list.toArray(ret);
		return ret;
	}

	public String getClientName() {
		return clientName;
	}

	public String getDefaultCharset() {
		return config.getString("client.options.defaultCharset", "UTF-8");
	}

	public void setDefaultCharset(String defaultCharset) {
		config.setValue("client.options.defaultCharset", defaultCharset);
	}

	public String getDefaultLanguage() {
		return config.getString("client.options.defaultLanguage", "en");
	}

	public void setDefaultLanguage(String defaultLanguage) {
		config.setValue("client.options.defaultLanguage", defaultLanguage);
	}

	public String getUserAgent() {
		return config.getString("client.options.userAgent",
				RequestHeaders.USER_AGENTs.IE7);
	}

	public void setUserAgent(String userAgent) {
		config.setValue("client.options.userAgent", userAgent);
	}

	public int getFailRepeatTimes() {
		return config.getInt("client.options.fail.repeat.times", 3);
	}

	public void setFailRepeatTimes(int times) {
		config.setValue("client.options.fail.repeat.times", String
				.valueOf(times));
	}

	public boolean isCookieEnabled() {
		return config.getBoolean("client.options.cookie.enabled", true);
	}

	public void setCookieEnabled(boolean cookieEnabled) {
		config.setValue("client.options.cookie.enabled", String
				.valueOf(cookieEnabled));
	}

	public boolean isCookiePersist() {
		return config.getBoolean("client.options.cookie.persistent", true);
	}

	public void setCookiePersist(boolean cookiePersist) {
		config.setValue("client.options.cookie.persistent", String
				.valueOf(cookiePersist));
		ClientManager.getInstance().getCookieStorage(clientName).setPersistent(
				cookiePersist);
	}

	public boolean isCacheEnabled() {
		return config.getBoolean("client.options.cache.enabled", false);
	}

	public void setCacheEnabled(boolean cacheEnabled) {
		config.setValue("client.options.cache.enabled", String
				.valueOf(cacheEnabled));
	}

	public CacheConfig getCacheConfig() {
		CacheConfig ret = new CacheConfig();
		ret.setSharedCache(config.getBoolean("client.options.cache.isShared",
				false));
		ret.setHeuristicCachingEnabled(config.getBoolean(
				"client.options.cache.isHeuristicEnabled", false));
		ret.setMaxObjectSizeBytes(config.getInt(
				"client.options.cache.maxObjectSizeBytes", ret
						.getMaxObjectSizeBytes()));
		ret.setMaxCacheEntries(config.getInt(
				"client.options.cache.maxCacheEntries", ret
						.getMaxCacheEntries()));
		ret.setHeuristicCoefficient(config.getFloat(
				"client.options.cache.heuristicCoefficient", ret
						.getHeuristicCoefficient()));
		ret.setHeuristicDefaultLifetime(config.getLong(
				"client.options.cache.heuristicDefaultLifetime", ret
						.getHeuristicDefaultLifetime()));
		ret.setAsynchronousWorkersMax(config.getInt(
				"client.options.cache.asynchronousWorkersMax", ret
						.getAsynchronousWorkersMax()));
		ret.setAsynchronousWorkersCore(config.getInt(
				"client.options.cache.asynchronousWorkersCore", ret
						.getAsynchronousWorkersCore()));
		ret.setAsynchronousWorkerIdleLifetimeSecs(config.getInt(
				"client.options.cache.asynchronousWorkerIdleLifetimeSecs", ret
						.getAsynchronousWorkerIdleLifetimeSecs()));
		ret.setRevalidationQueueSize(config.getInt(
				"client.options.cache.revalidationQueueSize", ret
						.getRevalidationQueueSize()));
		return ret;
	}

	public void setCacheConfig(CacheConfig cacheConf) {
		config.setValue("client.options.cache.isShared", String
				.valueOf(cacheConf.isSharedCache()));
		config.setValue("client.options.cache.isHeuristicEnabled", String
				.valueOf(cacheConf.isHeuristicCachingEnabled()));
		config.setValue("client.options.cache.maxObjectSizeBytes", String
				.valueOf(cacheConf.getMaxObjectSizeBytes()));
		config.setValue("client.options.cache.maxCacheEntries", String
				.valueOf(cacheConf.getMaxCacheEntries()));
		config.setValue("client.options.cache.heuristicCoefficient", String
				.valueOf(cacheConf.getHeuristicCoefficient()));
		config.setValue("client.options.cache.heuristicDefaultLifetime", String
				.valueOf(cacheConf.getHeuristicDefaultLifetime()));
		config.setValue("client.options.cache.asynchronousWorkersMax", String
				.valueOf(cacheConf.getAsynchronousWorkersMax()));
		config.setValue("client.options.cache.asynchronousWorkersCore", String
				.valueOf(cacheConf.getAsynchronousWorkersCore()));
		config.setValue(
				"client.options.cache.asynchronousWorkerIdleLifetimeSecs",
				String.valueOf(cacheConf
						.getAsynchronousWorkerIdleLifetimeSecs()));
		config.setValue("client.options.cache.revalidationQueueSize", String
				.valueOf(cacheConf.getRevalidationQueueSize()));
	}

	public HttpHost getProxyHost() {
		String str = config.getString("client.options.proxyHost", null);
		return makeHttpHost(str);
	}

	public void setProxyHost(HttpHost proxyHost) {
		if (proxyHost != null)
			config.setValue("client.options.proxyHost", proxyHost.toString());
	}

	public String getRawValue(String key) {
		return config.getValue(key);
	}

	public void setRawValue(String key, String value) {
		config.setValue(key, value);
	}

	public void loadFromStream(InputStream is) throws IOException {
		config.load(is);
	}

	public void saveToStream(OutputStream os) throws IOException {
		config.save(os);
	}

	public void load() throws IOException {
		String path = ClientManager.getInstance().getClientPath(clientName)
				+ "/" + DEFAULT_CONFIG_FILE;
		if (new File(path).exists()) {
			InputStream is = new FileInputStream(path);
			loadFromStream(is);
			is.close();
		}
	}

	public void save() throws IOException {
		String path = ClientManager.getInstance().getClientPath(clientName);
		new File(path).mkdirs();
		path += "/" + DEFAULT_CONFIG_FILE;
		OutputStream os = new FileOutputStream(path);
		saveToStream(os);
		os.close();
	}

}
